package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.ResourceDto;
import com.zzyl.entity.Resource;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.service.ResourceService;
import com.zzyl.utils.NoProcessing;
import com.zzyl.utils.ThreadLocalUtil;
import com.zzyl.vo.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;
    final StringRedisTemplate stringRedisTemplate;


    /**
     * 根据用户id查询对应的资源数据
     *
     * @return
     */
    @Override
    public List<MenuVo> getMyMenus() {
        Long userId = ThreadLocalUtil.get();
        //查询数据
        List<MenuVo> menuVoList = resourceMapper.selectMenuByUserId(userId);
        //构建 parentNo -> List<MenuVo> 的映射
        Map<String, List<MenuVo>> parentMap = menuVoList.stream().collect(Collectors.groupingBy(MenuVo::getParentResourceNo));

        //递归构建我的菜单信息
        buildTree(menuVoList, parentMap);
        return parentMap.get(SuperConstant.ROOT_RESOURCE_PARENT_ID);
    }


    /**
     * 递归构建我的菜单信息（已提供）
     */
    private void buildTree(List<MenuVo> menuVoList, Map<String, List<MenuVo>> parentMap) {
        menuVoList.forEach(menuVo -> {
            //补全数据
            menuVo.setMeta(MenuMetaVo.builder().title(menuVo.getName()).build());
            menuVo.setRedirect("/" + menuVo.getName());
            //根据当前资源编号查询子资源
            List<MenuVo> childrenList = parentMap.get(menuVo.getResourceNo());
            if (CollUtil.isNotEmpty(childrenList)) {
                buildTree(childrenList, parentMap);
                menuVo.setChildren(childrenList);
            }
        });
    }


    /**
     * 资源列表展示
     *
     * @param dto
     * @return
     */
    @Override
    public List<ResourceVo> listResource(ResourceDto dto) {
        List<Resource> resources = resourceMapper.selectList(dto);
        return BeanUtil.copyToList(resources, ResourceVo.class);
    }


    /**
     * 资源树形展示
     *
     * @return
     */
    @Override
    //    将方法返回值加入缓存，命名为cacheNames+::+key,自动转为String类型
    @Cacheable(cacheNames = "resource", key="#root.methodName")
    public TreeVo treeResource() {

//        1.设置资源dto的状态，只查找资源列表，不查找按钮列表（不然会将button的资源也给添加到属性结构数据）
        ResourceDto resourceDto = ResourceDto.builder()
                .resourceType("m")
                .build();

        // 2. 查询所有资源数据，（不区分类型，上面给出状态区分）
        List<Resource> resources = resourceMapper.selectList(resourceDto);

        // 3. 查询根节点，即没有父节点的资源数据（根节点）  （信息在数据库）
        Resource rootResource = CollUtil.findOneByField(resources, "resourceNo", SuperConstant.ROOT_RESOURCE_PARENT_ID);

        // 4. 调用递归方法，构建根节点的树形结构数据
        TreeItemVo treeItemVo = recursionTreeItemVo(rootResource, resources);

        // 6. 构建返回结果，将根节点的数据添加到items中返回
        return TreeVo.builder()
                .items(List.of(treeItemVo))
                .build();
    }


    /**
     * 构建树形结构，递归调用
     *
     * @param rootResource
     * @param resources
     */
    private TreeItemVo recursionTreeItemVo(Resource rootResource, List<Resource> resources) {
//        1.设置根节点信息 (Children设置不了，需遍历后才有，在此就没有设置）
        TreeItemVo treeItemVo = TreeItemVo.builder()
                .id(rootResource.getResourceNo())
                .label(rootResource.getResourceName())
                .build();

//        2.从集合中查找根节点的子节点生成新集合
        Collection<Resource> childrenList = CollUtil.filterNew(resources, resource -> resource.getParentResourceNo().equals(rootResource.getResourceNo()));

//        3.将新的子节点集合转换成树节点的集合
        List<TreeItemVo> children = childrenList.stream().map(resource -> recursionTreeItemVo(resource, resources)).collect(Collectors.toList());

//        4.给根节点设置子节点集合（前面builder不了，后面补上）
        treeItemVo.setChildren(children);

//        5.返回一棵树型结构数据
        return treeItemVo;
    }


    /**
     * 新增资源  资源编号前端不传，后端自己凭工具类生成
     *
     * @param resourceDto
     */
    @Override
    public void addResource(ResourceDto resourceDto) {
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);

//        1.查询资源的父节点信息
        Resource parentResource = resourceMapper.selectByResourceNo(resource.getParentResourceNo());

//        2.调用方法得到资源编号
        String resourceNo = createResourceNo(resource, parentResource);

        // 3. 补全新增资源的数据
        resource.setResourceNo(resourceNo);
        resourceMapper.insert(resource);
    }


    /**
     * 资源编号生成规则 : 15位字符串，3位为一组，先查资源父节点有无子节点，无就截取前有效位在后面添加，有就遍历其子节点资源编号获取最大值，在其基础上加
     * 具体内容在工具类中查看-->NoProcessing
     */
    private String createResourceNo(Resource resource, Resource parentResource) {
        //判断是否是按钮，如果是按钮，则不限制层级，不是按钮就是资源，按照层级来分
        if (!StrUtil.equals(resource.getResourceType(), "r")) {
            if (NoProcessing.processString(parentResource.getResourceNo()).length() / 3 >= 5) {
                //判断资源编号是否大于三级
                //100 001 000 000 000
                //100 001 001 000 000
                //100 001 001 001 000
                //100 001 001 001 001 001
                throw new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
            }
        }

        //根据父资源编号查询子资源
        ResourceDto dto = ResourceDto.builder().parentResourceNo(parentResource.getResourceNo()).build();
        List<Resource> resources = resourceMapper.selectList(dto);
        if (ObjectUtil.isEmpty(resources)) {
            //无下属节点，创建新的节点编号  100 001 001 001 000--->100 001 001 001 001
            return NoProcessing.createNo(parentResource.getResourceNo(), false);
        } else {
            //有下属节点，在已有的节点上追加，先获取已有节点的最大值--100001003000000
            Long maxNo = resources.stream().map(r -> Long.valueOf(r.getResourceNo())).max(Comparator.comparing(i -> i)).get();
            return NoProcessing.createNo(String.valueOf(maxNo), true);
        }
    }


    /**
     * 修改资源信息
     *
     * @param resourceDto
     */
    @Override
    public void updateResource(ResourceDto resourceDto) {
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);

        resourceMapper.updateByPrimaryKeySelective(resource);
    }

    /**
     * 启用禁用资源
     *
     * @param resourceDto
     */
    @Override
    public void enableResource(ResourceDto resourceDto) {
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);
        resourceMapper.updateByResourceNo(resource.getResourceNo(), resource.getDataState());
    }

    /**
     * 删除资源
     *
     * @param menuId
     */
    @Override
    public void deleteMenu(String menuId) {
//        1.前端校验了启用状态下不可删除菜单，也就是resourceNo

//        2. 删除该menuId的资源数据
        resourceMapper.deleteByResourceNo(menuId);

//        3.删除其子节点 (按钮资源也删掉)
        List<Resource> resources = resourceMapper.selectList(new ResourceDto());
        recursionDeleteChildrenResource(menuId, resources);

    }

    /**
     * 递归删除父节点下面子节点
     *
     * @param menuId
     * @param resources
     */
    private void recursionDeleteChildrenResource(String menuId, List<Resource> resources) {
//        1.根据父节点获取其子节点
        List<Resource> childrenList = new ArrayList<>(CollUtil.filterNew(resources, resource -> resource.getParentResourceNo().equals(menuId)));

        // 2.递归出口
        if (CollectionUtil.isEmpty(childrenList)) {
            return;
        }


//        2. resource集合映射成resourceNo资源编号的集合
       /* childrenList.stream()
                .map(Resource::getResourceNo)
                .forEach(resourceNo -> {
                    resourceMapper.deleteByResourceNo(resourceNo);
                    deleteChildrenResource(resourceNo, childrenList);
                });*/

        childrenList.forEach(resource -> {
            recursionDeleteChildrenResource(resource.getResourceNo(), childrenList);
            resourceMapper.deleteByResourceNo(resource.getResourceNo());
        });


    }


}
